Effective-ObjectiveC 笔记 Chapter1

####Chapter 1

###Item 1 先好好了解一下一下Objc或者C的语法

  • 了解指针类型与值类型。
  • 了解C语言的核心知识,对了解内存模型和指针有帮助

###Item 2 在类的头文件中尽量少引入其他头文件

  • 在.h文件中尽量少取import其他.h文件
  • 尽量用@class 在.h文件声明一下需要用到的类
  • 如果需要继承一个父类,需要在子类.h文件中import父类的.h文件
  • 如果一个.h文件中只有很简单的protocol,可以把这个.h文件包含进来
  • 尽可能晚地将其他.h文件import进来,即尽量在.m文件中引用其他.h文件
  • 协议尽量放在.m文件里面的class-continuation category里面声明要实现它

建议这样做:

EOCEmployer.h

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>

@class EOCEmployer;//建议使用@class

@interface EOCPerson : NSObject
@property (nonatomic, strong) EOCEmployer *employer;
@end

EOCEmployer.m

1
2
3
4
5
6
7
8
#import "EOCPerson.h"
#import "EOCCP1Delegate.h"//建议在这里import

@interface EOCPerson()<EOCCP1Delegate> //建议这里声明需要实现它
@end

@implementation EOCPerson
@end

EOCPerson.h

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>

@class EOCPerson;

@interface EOCEmployer : NSObject
- (void) addEmployer:(EOCPerson*)person;
@end

EOCStudent.h,这个类继承了EOCPerson

1
2
3
4
5
#import <Foundation/Foundation.h>
#import "EOCPerson.h"//父类在这里需要import.h文件

@interface EOCStudent : EOCPerson
@end
1
2
3
4
5
6
7
#import <Foundation/Foundation.h>
//非要在某个类的.h文件声明实现这个protocol,建议像这样单个.h文件里面写单个Protocol
@protocol EOCCP1Delegate <NSObject>

@optional
- (void) eoc_cp1Function;
@end

###Item 3 多用字面量语法,少用与字面量等价的构造方法或者类方法

  • 建议使用字面量语法(Literal Syntax) 来创建NSString,NSNumber,NSArray,NSDictionray对象
  • 使用下标来访问NSArray的index或者NSDictionary的Key
  • 在字面量语法中如果有nil值,会抛出异常,使用字面量语法要保证插入的值不为nil
  • 限制性:只能插入Foundation框架中的对象,自定义的对象只能用非字面量语法插入

推荐用法

NSString :

1
2
//NSString字面量语法
NSString *string = @"string";

NSNumber:

1
2
3
4
5
6
NSNumber *integerNumber = @1;
NSNumber *floatNumber = @1.2f;
NSNumber *doubleNumber = @1.2;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
NSNumber *plusNumber = @(1.1f + 1);

NSArray与NSMutableArray:

1
2
3
4
5
6
7
8
//注意,并不能@[@"a1",nil];这样使用,会崩。使用字面量语法容易崩溃,但更容易定位bug
//像@"a1"这种变量,只能使用Foundation框架里面的类型,要使用custom的类对象,只能使用非字面量方法

NSArray *array = @[@"a1",@"a2",@"a3"];
NSString *a3 = array[1];

NSMutableArray *mulArray = [@[@"a1",@"a2",@"a3"] mutableCopy];
mulArray[0] = @"a0";

NSDictionary与NSMutableDictionary:

1
2
3
4
5
NSDictionary *dic = @{@"k1" : @"1" , @"k2" : @2};
NSString *k1 = dic[@"k1"];

NSMutableDictionary *mulDic = [@{@"k1" : @"1" , @"k2" : @2} mutableCopy];
mulDic[@"k1"] = @"0";

###Item4 多用有具体类型的常量,少用#define来定义常量

  • 避免去使用#define定义常量,它们不具备具体类型,只是做了单纯替代。而且它们容易在没有任何警告的情况下被重复定义,从而产生不稳定的值。
  • 定义translation-unit-specific(可以理解为定义只在某个.m里面供内部使用的)的常量,要加上static const关键字,这样常量就不会定义在global symbol table,避免了不同.m文件中定义同样名字的常量造成冲突报错的问题。所以不需对它命名添加前缀。
  • 如果要定义一个供外部使用的常量,在.h文件中使用extern关键字来定义。在.m文件中具体再指定具体值。.h文件使用extern来修饰的常量,会在global symbol table定义,所以要加入前缀来避免冲突。

假设要定义一个专门放NSNotification名字的文件

定义仅供内部使用的常量

EOCItem4.h:

1
2
@interface EOCItem4 : NSObject
@end

EOCItem4.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import "EOCItem4.h"
#import "EOCItem4Notifications.h"

//定义仅供内部使用的常量,使用static 与 const常量来保证不声明在global symbol table,避免了冲突
static NSString *const constString = @"string";
static const CGFloat constFloat = 1.0f;

@implementation EOCItem4

- (void) item3Func {
NSLog(@"%@",constString);
NSLog(@"%f",constFloat);
NSLog(@"%@",EOCItem4Notifications1);//在其他文件定义的对外使用的常量
}

@end

定义对外使用的常量

1
2
3
4
5
6
7
8
9
10
11
12
13
//在EOCItem4Notifications.h中

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

extern NSString *const EOCItem4Notifications1;
extern NSString *const EOCItem4Notifications2;

///在EOCItem4Notifications.m 中
#import "EOCItem4Notifications.h"

NSString *const EOCItem4Notifications1 = @"EOCItem4Notifications1";
NSString *const EOCItem4Notifications2 = @"EOCItem4Notifications2";

###Item5 使用枚举去定义一些状态和选项

  • 使用enum去定义一些状态和选项,使他们可读性更强
  • 若使用enum去定义去定义一些可以一并使用选项型的常量,把它们的值定义为2的幂值,这样它们就可以用 |运算符来一起使用了
  • NS_ENUM或者NS_OPTIONS来定义枚举,并且显示给出它的类型
  • 当使用switch来判断一个枚举值的时候,建议不要写default,这样当增加一个新枚举时候编译器就会报警告。有时候并不需要去判断这个新增的枚举值,如果有default新增的枚举值就会走default这里,没有警告可能会使我们错误地通过default处理这个状态得会造成一些Bug。

定义了2个枚举,一个用作表示状态,一个用于选项型:

1
2
3
4
5
6
7
8
9
10
11
12
typedef NS_ENUM(NSUInteger,EOCItem5State) {
EOCItem5StateNone = 0 ,
EOCItem5State1 ,
EOCItem5State2 = 75,
EOCItem5State3 , //后来才新增的状态
};

typedef NS_OPTIONS(NSUInteger, EOCItem5Option) {
EOCItem5OptionA = 1 << 0,
EOCItem5OptionB = 1 << 1,
EOCItem5OptionC = 1 << 2,
};

状态型枚举使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
EOCItem5State state = EOCItem5StateNone;

//switch产生了 Enumeration value 'EOCItem5State' not handled in switch 警告
switch (state) {//让这里产生警告,假设只需要处理这3个,新增的状态3就不处理了,如果错误通过default去处理,就会产生Bug
case EOCItem5StateNone:
break;
case EOCItem5State1:
break;
case EOCItem5State2:
break;
// default:
//处理default。。。
// break;
}

选项型枚举使用:

1
2
3
4
5
6
7
8
9
10
11
EOCItem5Option option = EOCItem5OptionA | EOCItem5OptionB | EOCItem5OptionC;

if (option & EOCItem5OptionA ) {
//option中有选EOCItem5OptionA
}
if (option & EOCItem5OptionB) {
//option中有选EOCItem5OptionB
}
if (option & EOCItem5OptionC) {
//option中有选EOCItem5OptionC
}